{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cbefbd26-1838-4234-b04f-dc5d28eed1de",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "from python_speech_features import mfcc\n",
    "import scipy.io.wavfile as wav\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import os\n",
    "\n",
    "print(1%10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "899b8c55-cc99-460a-a6cf-e8fb89c2c571",
   "metadata": {},
   "outputs": [],
   "source": [
    "#计算mfcc\n",
    "def cul_mfcc(audio_path):\n",
    "    sr, y = wav.read(audio_path)\n",
    "    mfcc_features = mfcc(\n",
    "        signal=y,\n",
    "        samplerate=sr,\n",
    "        preemph=0.97,    # 预加重系数\n",
    "        winlen=0.032,    # 帧长（秒）\n",
    "        winstep=0.016,    # 帧移（秒）\n",
    "        numcep=13,       # MFCC 系数数量\n",
    "        nfilt=26,        # Mel 滤波器数量\n",
    "        nfft=512,        # FFT 窗口大小\n",
    "        winfunc=np.hamming,\n",
    "    )\n",
    "    #print(mfcc_features)\n",
    "    return mfcc_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "80e7b80d-0a32-4fe6-bbb2-ebd0c20a0696",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['black', 'close', 'nowav', 'open', 'zhang']\n",
      "(62, 13)\n"
     ]
    }
   ],
   "source": [
    "##导入数据\n",
    "data_dir=\"user-data-2\\\\speech2\"\n",
    "labels=os.listdir(data_dir)\n",
    "print(labels)\n",
    "def load_data():\n",
    "    dataList=[]\n",
    "    labelList=[]\n",
    "    valList=[]\n",
    "    valLabList=[]\n",
    "    testList=[]\n",
    "    testLabList=[]\n",
    "    labelIndex=0\n",
    "    for name in labels:\n",
    "         \n",
    "         dis=os.path.join(data_dir, name)\n",
    "         names=os.listdir(dis)\n",
    "         for i in range(0,len(names)):\n",
    "            nm=names[i]\n",
    "            path=os.path.join(dis, nm)\n",
    "            data=cul_mfcc(path)\n",
    "            if i%10==8:\n",
    "                valList.append(data)\n",
    "                valLabList.append(labelIndex)\n",
    "            elif i%10==9:\n",
    "                testList.append(data)\n",
    "                testLabList.append(labelIndex)\n",
    "            else:\n",
    "                dataList.append(data)\n",
    "                labelList.append(labelIndex)\n",
    "         labelIndex=labelIndex+1\n",
    "    return (np.array(dataList,dtype=np.float32),np.array(labelList)),(np.array(valList,dtype=np.float32),np.array(valLabList)),(np.array(testList,dtype=np.float32),np.array(testLabList))\n",
    "\n",
    "#获取训练集,校验集,测试集\n",
    "data,valid,test=load_data()\n",
    "print(data[0][0].shape)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "80e7b80d-0a32-4fe6-bbb2-ebd0c20a0695",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "num_labels 5\n",
      "input_shape: (62, 13)\n",
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      " Layer (type)                Output Shape              Param #   \n",
      "=================================================================\n",
      " reshape (Reshape)           (None, 62, 13, 1)         0         \n",
      "                                                                 \n",
      " conv2d (Conv2D)             (None, 60, 11, 8)         80        \n",
      "                                                                 \n",
      " batch_normalization (BatchN  (None, 60, 11, 8)        32        \n",
      " ormalization)                                                   \n",
      "                                                                 \n",
      " max_pooling2d (MaxPooling2D  (None, 30, 5, 8)         0         \n",
      " )                                                               \n",
      "                                                                 \n",
      " flatten (Flatten)           (None, 1200)              0         \n",
      "                                                                 \n",
      " dense (Dense)               (None, 16)                19216     \n",
      "                                                                 \n",
      " dense_1 (Dense)             (None, 5)                 85        \n",
      "                                                                 \n",
      "=================================================================\n",
      "Total params: 19,413\n",
      "Trainable params: 19,397\n",
      "Non-trainable params: 16\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "\n",
    "from tensorflow.keras import layers\n",
    "from tensorflow.keras import models\n",
    "from tensorflow.keras import regularizers\n",
    "\n",
    "num_labels = len(labels)\n",
    "print(\"num_labels\",num_labels)\n",
    "input_shape=data[0][0].shape\n",
    "print(\"input_shape:\",input_shape)\n",
    "\n",
    "model = models.Sequential([\n",
    "    layers.Input(shape=input_shape),\n",
    "    layers.Reshape((*input_shape, 1)),  # 添加通道维度\n",
    "    layers.Conv2D(8, (3, 3), activation='relu'),\n",
    "    layers.BatchNormalization(),  # 添加BN层稳定分布\n",
    "    layers.MaxPooling2D((2, 2)),\n",
    "    layers.Flatten(),\n",
    "    #layers.Dense(16, activation='relu',kernel_regularizer=regularizers.l2(0.001)),,\n",
    "    layers.Dense(16, activation='relu'),\n",
    "    layers.Dense(num_labels, activation='softmax')\n",
    "])\n",
    "\n",
    "# model = models.Sequential([\n",
    "#     layers.Input(shape=input_shape),\n",
    "#     layers.Reshape((*input_shape, 1)),  # 添加通道维度\n",
    "#     layers.Conv2D(8, (3,3), activation='relu'),\n",
    "#     #layers.BatchNormalization(),  # 添加BN层稳定分布\n",
    "#     layers.MaxPooling2D((2,2)),\n",
    "#     layers.Conv2D(16, (3,3), activation='relu'),\n",
    "#     layers.Flatten(),\n",
    "#     layers.Dense(32, activation='relu'),\n",
    "#     #layers.Dropout(0.3),\n",
    "#     layers.Dense(num_labels, activation='softmax')\n",
    "#  ])\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "560daa3c-6596-4d29-9006-c8c74b1179f7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "data: <class 'numpy.ndarray'> <class 'tuple'>\n",
      "Epoch 1/20\n",
      "28/28 [==============================] - 2s 25ms/step - loss: 0.8345 - accuracy: 0.6244 - val_loss: 0.5403 - val_accuracy: 0.7778\n",
      "Epoch 2/20\n",
      "28/28 [==============================] - 0s 13ms/step - loss: 0.2039 - accuracy: 0.9224 - val_loss: 0.1336 - val_accuracy: 0.9537\n",
      "Epoch 3/20\n",
      "28/28 [==============================] - 0s 13ms/step - loss: 0.0470 - accuracy: 0.9897 - val_loss: 0.0148 - val_accuracy: 1.0000\n",
      "Epoch 4/20\n",
      "28/28 [==============================] - 0s 15ms/step - loss: 0.0203 - accuracy: 0.9977 - val_loss: 0.0106 - val_accuracy: 1.0000\n",
      "Epoch 5/20\n",
      "28/28 [==============================] - 0s 13ms/step - loss: 0.0120 - accuracy: 0.9989 - val_loss: 0.0089 - val_accuracy: 1.0000\n",
      "Epoch 6/20\n",
      "28/28 [==============================] - 0s 14ms/step - loss: 0.0081 - accuracy: 1.0000 - val_loss: 0.0104 - val_accuracy: 1.0000\n",
      "Epoch 7/20\n",
      "28/28 [==============================] - 0s 14ms/step - loss: 0.0060 - accuracy: 1.0000 - val_loss: 0.0071 - val_accuracy: 1.0000\n",
      "Epoch 8/20\n",
      "28/28 [==============================] - 0s 14ms/step - loss: 0.0047 - accuracy: 1.0000 - val_loss: 0.0068 - val_accuracy: 1.0000\n",
      "Epoch 9/20\n",
      "28/28 [==============================] - 0s 14ms/step - loss: 0.0037 - accuracy: 1.0000 - val_loss: 0.0074 - val_accuracy: 1.0000\n",
      "Epoch 10/20\n",
      "28/28 [==============================] - 0s 14ms/step - loss: 0.0030 - accuracy: 1.0000 - val_loss: 0.0075 - val_accuracy: 1.0000\n",
      "Epoch 10: early stopping\n"
     ]
    }
   ],
   "source": [
    "# model.compile(\n",
    "#     optimizer=tf.keras.optimizers.Adam(),\n",
    "#     #loss=tf.keras.losses.CategoricalCrossentropy(),\n",
    "#     loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "#     metrics=['accuracy'],\n",
    "# )\n",
    "model.compile(optimizer='adam',\n",
    "              loss='sparse_categorical_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "EPOCHS = 20\n",
    "#print(\"train_spectrogram_ds:\",valid)\n",
    "print(\"data:\",type(data[0]),type(data))\n",
    "history = model.fit(\n",
    "    data[0],data[1],\n",
    "    validation_data=valid,\n",
    "    epochs=EPOCHS,\n",
    "    callbacks=tf.keras.callbacks.EarlyStopping(verbose=1, patience=2),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2b4e4cc3-b494-4a4c-bb8c-781f2254bbf9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4/4 [==============================] - 0s 7ms/step - loss: 0.0335 - accuracy: 0.9907\n",
      "4/4 [==============================] - 0s 6ms/step\n"
     ]
    },
    {
     "data": {
      "image/png": "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CgoKcl5n7969bsMoJalRo0amfz8gu0gybkKHDh3S3Xffrdtuu01Lly7V1q1bNX36dElyjsMuUKDAda9Tp04dlShRQvPmzWNcdi67kf65zDAMtwTjcpv07y8Ut9xyi1atWqWvv/5a1atX17Rp01S1alUlJibK4XBIkmbPnq0dO3Y4t127djl/4UTOCAgIcPtss9nkcDgy7U/JvZ9btWqlf/75R9u2bdP69evVunVrRUVFad26dVq7dq1KliypatWqSZI2b96sLl266K677tIXX3yh7du3a/jw4bp48aLz2uHh4WrTpo0WLVok6d85HY899lhOfXXI/Rm9sj2z/nd1eb/D4dAzzzzj9uz+9NNP2r9/v9svsFf7s4acN2bMGH3zzTdavny5goKCnO2Z9Ynr/2d79uyprVu3avLkyfr++++1Y8cOFStWzO25vdp1Lvfttf7fAHgDkgwvcuUvfZs3b1blypV1yy23uLXHx8crPT1db775ppo0aaIqVarozz//dDumVq1aWrNmzTXvV6lSJa1du1aff/65+vfvb86XwA25PFHven0k/fu33N9//73b/zy+//57BQUFqVSpUpL+/R9P8+bNNXr0aG3fvl358uXTsmXLFBoaqlKlSum3335TZGSk21ahQoUc+364uurVqyspKUm///67s+2XX37R6dOnnYlDSEiI6tSpo7fffls2m03Vq1dXy5YttX37dn3xxRduVYyNGzeqXLlyGj58uBo0aKDKlSvr0KFDGe7brVs3LV68WJs2bdKBAwfUpUuXnP+yfiwyMlL58uXThg0bnG1paWmKj4939rPk/t/9kydPat++fbr11lslSfXq1dPu3bszPLuXrw1rLV26VK+++qqWLFnilvTdiPXr12vAgAG6++67VaNGDdntdh0/fjxL17j11ludLxK4LD4+PkvXAHISSYYX+f333zVkyBDt3btXH374oaZNm6aBAwdmOK5SpUpKT0/XtGnT9Ntvv+n999/XO++843ZMdHS0tmzZor59+2rnzp3as2ePZs6cmeE/YlWqVNHatWudQ6eQO/Lnz69hw4Zp6NChWrBggQ4cOKDNmzdrzpw5GY7t27evfv/9d/Xv31979uzR559/rpEjR2rIkCHKkyePfvjhB40bN07x8fFKSkrSp59+qmPHjjl/kRk1apRiYmI0ZcoU7du3Tz///LPm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\n",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import seaborn as sns\n",
    "model.evaluate(test[0],test[1], return_dict=True)\n",
    "y_pred = model.predict(test[0])\n",
    "y_pred = tf.argmax(y_pred, axis=1)\n",
    "y_true = test[1]\n",
    "confusion_mtx = tf.math.confusion_matrix(y_true, y_pred)\n",
    "plt.figure(figsize=(10, 8))\n",
    "sns.heatmap(confusion_mtx,\n",
    "            xticklabels=labels,\n",
    "            yticklabels=labels,\n",
    "            annot=True, fmt='g')\n",
    "plt.xlabel('Prediction')\n",
    "plt.ylabel('Label')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "274c685c-b66c-4530-90de-901e2cf26049",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:absl:Found untraced functions such as _jit_compiled_convolution_op, _update_step_xla while saving (showing 2 of 2). These functions will not be directly callable after loading.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: C:\\Users\\zrz\\AppData\\Local\\Temp\\tmpvtz6k3ac\\assets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: C:\\Users\\zrz\\AppData\\Local\\Temp\\tmpvtz6k3ac\\assets\n",
      "D:\\anaconda3\\envs\\tensor37\\lib\\site-packages\\tensorflow\\lite\\python\\convert.py:765: UserWarning: Statistics for quantized inputs were expected, but not specified; continuing anyway.\n",
      "  warnings.warn(\"Statistics for quantized inputs were expected, but not \"\n"
     ]
    }
   ],
   "source": [
    "# 保存模型\n",
    "model.save('speech_model.h5')\n",
    "import tensorflow as tf\n",
    "\n",
    "# 加载模型\n",
    "model = tf.keras.models.load_model('speech_model.h5')\n",
    "\n",
    "# 转换为 TFLite 格式\n",
    "#converter = tf.lite.TFLiteConverter.from_keras_model(model)\n",
    "#converter.optimizations = [tf.lite.Optimize.DEFAULT]  # 启用量化\n",
    "#tflite_model = converter.convert()\n",
    "\n",
    "\n",
    "\n",
    "def representative_data_gen():\n",
    "    for input_value in tf.data.Dataset.from_tensor_slices(data[0]).batch(1).take(400):\n",
    "        yield [input_value]\n",
    "\n",
    "converter = tf.lite.TFLiteConverter.from_keras_model(model)\n",
    "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
    "converter.representative_dataset = representative_data_gen\n",
    "#converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]\n",
    "#converter.inference_input_type = tf.int8\n",
    "converter.inference_output_type = tf.int8\n",
    "tflite_model = converter.convert()\n",
    "\n",
    "\n",
    "\n",
    "# def representative_data_gen():\n",
    "#     for _ in range(100):  # 使用 100 个样本进行校准\n",
    "#         yield [data[0][_:_+1].astype(np.float32)]  # X_train 是你的训练数据\n",
    "\n",
    "# converter = tf.lite.TFLiteConverter.from_keras_model(model)\n",
    "# converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
    "# converter.representative_dataset = representative_data_gen\n",
    "# converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]  # 强制使用 8 位整数\n",
    "# #converter.inference_input_type = tf.uint8  # 输入为 8 位整数\n",
    "# converter.inference_output_type = tf.uint8  # 输出为 8 位整数\n",
    "# tflite_model = converter.convert()\n",
    "\n",
    "\n",
    "\n",
    "# 保存量化模型\n",
    "with open('test.tflite', 'wb') as f:\n",
    "    f.write(tflite_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d5904be1-64cf-42cc-8614-909d13b13367",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "62 13 <class 'numpy.ndarray'>\n",
      "[[-120  116 -127 -128 -125]]\n",
      "['black', 'close', 'nowav', 'open', 'zhang']\n"
     ]
    }
   ],
   "source": [
    "# 测试一下\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "\n",
    "\n",
    "\n",
    "interpreter=tf.lite.Interpreter('test.tflite')\n",
    "interpreter.allocate_tensors()\n",
    "input_dateils=interpreter.get_input_details()\n",
    "output_dateils=interpreter.get_output_details()\n",
    "\n",
    "spectrogram = cul_mfcc(\"E:/java-project/Wav/result/25test/close-1740198509414.wav\")\n",
    "#print(spectrogram)\n",
    "spectrogram=np.array(spectrogram,dtype=np.float32)\n",
    "print(len(spectrogram),len(spectrogram[0]),type(spectrogram))\n",
    "#扩展一维数组\n",
    "spectrogram = np.expand_dims(spectrogram, axis=0)\n",
    "#print(spectrogram)\n",
    "\n",
    "interpreter.set_tensor(input_dateils[0]['index'],spectrogram)\n",
    "interpreter.invoke()\n",
    "result=interpreter.get_tensor(output_dateils[0]['index'])\n",
    "print(result)\n",
    "print(labels)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e6fd19e6-30d4-47d8-91c9-007aff8cd0ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "24976\n",
      "128186\n",
      "完成。。。\n"
     ]
    }
   ],
   "source": [
    "#文件转换成c语言\n",
    "binary_data=[]\n",
    "with open('./test.tflite', 'rb') as f:\n",
    "    binary_data = f.read()\n",
    "\n",
    "fileSize=len(binary_data)\n",
    "print(fileSize)\n",
    "strResult='';\n",
    "strResult+='#ifndef model_h\\r\\n'\n",
    "strResult+='#define model_h 1\\r\\n'\n",
    "strResult+='const unsigned char model_tflite[] = {\\r\\n'\n",
    "myIndex=0\n",
    "for b in binary_data:\n",
    "    myIndex+=1\n",
    "    t='0X{:02X}'.format(b)\n",
    "    strResult+=t+','\n",
    "    if myIndex%16 ==0:\n",
    "       strResult+='\\r\\n' \n",
    "strResult+='};\\r\\n'\n",
    "strResult+='unsigned int out_tflite_len = '+str(fileSize)+';\\r\\n'\n",
    "strResult+=\"char*cmdName[]={\"\n",
    "for i in range(len(labels)-1):\n",
    "    strResult+=\"\\\"\"+labels[i]+\"\\\",\"\n",
    "strResult+=\"\\\"\"+labels[len(labels)-1]+\"\\\"\"\n",
    "strResult+=\"};\\r\\n\"\n",
    "\n",
    "strResult+='#endif\\r\\n'\n",
    "print(len(strResult))\n",
    "file_object = open('model.h', mode='wb')\n",
    "file_object.write(strResult.encode(\"utf-8\"))\n",
    "file_object.close()\n",
    "print(\"完成。。。\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da942860",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
